#include<iostream>
#include<unordered_map>
#include<pthread.h>
#include<sstream>
#include<fstream>
#include<vector>
#include"Socket.hpp"

const uint16_t defaultport=8888;
const std::string wwwroot="./wwwroot"; // web 根目录
const std::string sep = "\r\n";
const std::string homepage = "index.html";

class HttpServer;

class ThreadData
{
    public:
    ThreadData(int fd, HttpServer *s) : sockfd(fd), svr(s)
    {
    }

public:
    int sockfd;
    HttpServer *svr;
};

class  HttpRequest
{
public:
    void Deserialize(std::string req)
    {
        while(true)
        {
            std::size_t pos = req.find(sep);
            if(pos == std::string::npos) break;
            std::string temp = req.substr(0, pos);
            if(temp.empty()) break;
            req_header.push_back(temp);
            req.erase(0, pos+sep.size());
        }
        text = req;
    }
// .png:image/png 这个表示通常出现在MIME类型（Multipurpose Internet Mail Extensions）的上下文中，特别是在Web编程、文件传输或者邮件附件等场合。
// 这里的 .png 是文件的扩展名，它通常用于标识一个图像文件，该文件采用了PNG（Portable Network Graphics）格式。PNG是一种无损压缩的位图图像格式，通常用于网页和其他数字媒体。
// image/png 是MIME类型，它描述了文件的类型和内容。MIME类型是一个标准的标识，用来告诉接收方数据的性质。对于图像文件，MIME类型可以帮助浏览器、邮件客户端或其他软件正确地处理这些文件
// 内容识别：MIME类型允许接收方（例如浏览器）根据类型正确地解释和显示文件内容。如果没有MIME类型，接收方可能无法确定如何处理文件。
// 安全性：通过检查MIME类型，可以验证发送的内容是否与预期的类型匹配，从而有助于防范潜在的安全风险，例如MIME类型嗅探攻击。
// 互操作性：MIME类型为不同系统之间的数据交换提供了统一的格式和规则，提高了互操作性。
 // .png:image/png
    void Parse()
    {
        std::stringstream ss(req_header[0]);
        ss >> method >> url >> http_version;
        file_path = wwwroot; // ./wwwroot
        if(url == "/" || url == "/index.html") {
            file_path += "/";
            file_path += homepage; // ./wwwroot/index.html
        }
        else file_path += url; // /a/b/c/d.html->./wwwroot/a/b/c/d.html

        auto pos = file_path.rfind(".");
        if(pos == std::string::npos) suffix = ".html";
        else suffix = file_path.substr(pos);
    }
    
    void DebugPrint()
    {
        for(auto &line : req_header)
        {
            std::cout << "--------------------------------" << std::endl;
            std::cout << line << "\n\n";
        }

        std::cout << "method: " << method << std::endl;
        std::cout << "url: " << url << std::endl;
        std::cout << "http_version: " << http_version << std::endl;
        std::cout << "file_path: " << file_path << std::endl;
        std::cout << text << std::endl;
    }
public:
    std::vector<std::string> req_header;
    std::string text;

    // 解析之后的结果
    std::string method;
    std::string url;
    std::string http_version;
    std::string file_path; // ./wwwroot/a/b/c.html 2.png

    std::string suffix;
};

class HttpServer
{
public:
    HttpServer(uint16_t port=defaultport):port_(port)
    {

    }
    static void* Threadrun(void* args)
    {
        pthread_detach(pthread_self());//当线程是分离状态时，它结束时会自动释放其资源，不需要其他线程来调用 pthread_join()
        ThreadData* td=static_cast<ThreadData*>(args);
        td->svr->HandlerHttp(td->sockfd);
        delete td;
        return nullptr;

    }
    std::string SuffixToDesc(const std::string &suffix)
    {
        auto iter = content_type.find(suffix);
        if(iter == content_type.end()) return content_type[".html"];
        else return content_type[suffix];
    }
     static std::string ReadHtmlContent(const std::string &htmlpath)
    {
        // 坑
        std::ifstream in(htmlpath, std::ios::binary);
        if(!in.is_open()) return "";

        in.seekg(0, std::ios_base::end);
        auto len = in.tellg();
        in.seekg(0, std::ios_base::beg);

        std::string content;
        content.resize(len);

        in.read((char*)content.c_str(), content.size());
        in.close();

        return content;
    }
    void HandlerHttp(int sockfd)
    {
        char buffer[10240];
        ssize_t n=recv(sockfd,buffer,sizeof(buffer)-1,0);
        if(n>0)
        {
            buffer[n]=0;
            std::cout << buffer << std::endl; 
            HttpRequest req;
            req.Deserialize(buffer);//按行分开
            req.Parse();//对请求行做处理，主要提取url路径和判断并给到file_path
            // 返回响应的过程
            std::string text;
            bool ok = true;
            text = ReadHtmlContent(req.file_path); //对该路径下的文件内容做处理// 失败？
            if(text.empty())
            {
                ok = false;
                std::string err_html = wwwroot;
                err_html += "/";
                err_html += "err.html";
                text = ReadHtmlContent(err_html);
            }

            std::string response_line;
            if(ok)
                response_line = "HTTP/1.0 200 OK\r\n";
            else
                response_line = "HTTP/1.0 404 Not Found\r\n";
            
            //response_line = "HTTP/1.0 302 Found\r\n";
            std::string response_header = "Content-Length: ";
            response_header += std::to_string(text.size()); // Content-Length: 11
            response_header += "\r\n";
            response_header += "Content-Type: ";
            response_header += SuffixToDesc(req.suffix);
            response_header += "\r\n";
            response_header += "Set-Cookie: name=haha&&passwd=12345";
            response_header += "\r\n";

            //response_header += "Location: https://www.qq.com\r\n";
            std::string blank_line = "\r\n"; // \n

            std::string response = response_line;
            response += response_header;
            response += blank_line;
            response += text;

            send(sockfd, response.c_str(), response.size(), 0);
        }
        close(sockfd);
    }
    void Start()
    {
        listensock_. Socket();
        listensock_.Bind(port_);
        listensock_.Listen();
        for(;;)
        {
           std::string clientip;
            uint16_t clientport;
            int sockfd=listensock_.Accept(& clientip,&clientport);
            if (sockfd < 0)
                continue;
            lg(Info, "get a new connect, sockfd: %d", sockfd);
            pthread_t tid;
            ThreadData* td=new ThreadData(sockfd,this);
            pthread_create(&tid,0,Threadrun,td);
        }

    }
private:
Sock listensock_;
std::uint16_t port_;
std::unordered_map<std::string, std::string> content_type;
};
